package com.scut.dispatching_server.tool;

import com.scut.dispatching_server.config.GlobalVariables;
import com.scut.dispatching_server.model.RLOrderInfoForDispatching;
import com.scut.dispatching_server.model.RLTransferOrderValueState;
import com.scut.dispatching_server.service.DemandCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

@Component
public class DispatchingMatrixTool {

    private static ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private static DemandCache demandCache;

    @Autowired
    public void setDemandCache(DemandCache demandCache) {
        DispatchingMatrixTool.demandCache = demandCache;
    }

    @Autowired
    public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor){
        DispatchingMatrixTool.threadPoolTaskExecutor = threadPoolTaskExecutor;
    }

    public static ArrayList<Integer> getDriversByOrderId(int orderId, int[][] OrderDrivers){
        ArrayList<Integer> driverList = new ArrayList<Integer>();
        for(int i = 0; i <= OrderDrivers.length - 1; i++){
            if(OrderDrivers[i][orderId] == 1) {
                driverList.add(i);
            }
        }
        return driverList;
    }
    public static float getSumProbability(float[] probabilities){
        float sum = 0f;
        for(int probabilityIndex = 0; probabilityIndex < probabilities.length; probabilityIndex++){
            sum = sum + probabilities[probabilityIndex];
        }
        return sum;
    }
    public static ArrayList<Integer> getDemandIndexesBySupplierIndex(int supplierIndex, int[][] demandsSuppliersMatrix){
        ArrayList<Integer> demandIndexes = new ArrayList<Integer>();
        for(int i = 0; i <= demandsSuppliersMatrix[0].length - 1; i++){
            if(demandsSuppliersMatrix[supplierIndex][i] == 1){
                demandIndexes.add(i);
            }
        }
        return demandIndexes;
    }

    /**
     * 初始化订单分配矩阵，即为每个司机选择一个可以使得 P(R - V) 最大的订单
     * @param dispatchMatrix 全为 0 的订单分配矩阵
     * @param probabilitiesOneToOne 各司机对各订单在一对一情况下响应时间的概率分布
     * @param rlOrderInfos 数组，记录各订单已经等待的轮数以及各订单如果进入下一轮，其所能获得的长期收益
     * @return 二维数组，订单分配矩阵初始化的结果
     */
    public static int[][] rlInitDispatchMatrix(int[][] dispatchMatrix, float[][][] probabilitiesOneToOne, RLOrderInfoForDispatching[] rlOrderInfos){
        for(int i = 0; i < dispatchMatrix.length; i++){
            float maxGain = (float)0;
            int maxGainIndex = 0;
            for(int j = 0; j < dispatchMatrix[i].length; j++){
                float nowGain = (float)0;
                for(int k = 0; k < GlobalVariables.DISPATCHING_TIME_WINDOW; k++){
                    nowGain = nowGain + (GlobalVariables.REWARD_FUNCTION.get(rlOrderInfos[j].getWaitRound() *
                            GlobalVariables.DISPATCHING_TIME_WINDOW + k) - rlOrderInfos[j].getExpectedValue()) * probabilitiesOneToOne[i][j][k];
                }
                if(nowGain > maxGain){
                    dispatchMatrix[i][maxGainIndex] = 0;
                    maxGain = nowGain;
                    maxGainIndex = j;
                    dispatchMatrix[i][maxGainIndex] = 1;
                }
            }
        }
        return dispatchMatrix;
    }

    public static ArrayList<Integer> getDriversWhoDidNotReceiveThisOrder(int orderId, int[][] orderDriverMatrix){
        ArrayList<Integer> driversWhoDidNotReceiveThisOrder = new ArrayList<Integer>();
        for(int i = 0; i <= orderDriverMatrix.length - 1; i++){
            if(orderDriverMatrix[i][orderId] == 0){
                driversWhoDidNotReceiveThisOrder.add(i);
            }
        }
        return driversWhoDidNotReceiveThisOrder;
    }

    /**
     * 初始化订单分配矩阵，即首先为每个司机选择一个其最偏好的订单, 即可以使得 P * R 最大的订单
     * @param dispatchMatrix 全为 0 的订单分配矩阵
     * @param probabilitiesOnetoOne 各司机对各订单在一对一情况下响应时间的概率分布
     * @return 二维数组，订单分配矩阵初始化的结果
     */
    public static int[][] initDispatchMatrix(int[][] dispatchMatrix, float[][][] probabilitiesOnetoOne){
        for(int i = 0; i < dispatchMatrix.length; i++){
            float maxGain = (float)0;
            int maxGainIndex = 0;
            for(int j = 0; j < dispatchMatrix[i].length; j++){
                float nowGain = (float)0;
                for(int k = 0; k < GlobalVariables.GAIN.size(); k++){
                    nowGain = nowGain + GlobalVariables.GAIN.get(k) * probabilitiesOnetoOne[i][j][k];
                }
                if(nowGain > maxGain){
                    dispatchMatrix[i][maxGainIndex] = 0;
                    maxGain = nowGain;
                    maxGainIndex = j;
                    dispatchMatrix[i][maxGainIndex] = 1;
                }
            }
        }
        return dispatchMatrix;
    }

    public static void printOrderDriverMatrix(int[][] orderDriverMatrix){
        for(int driverIndex = 0; driverIndex < orderDriverMatrix.length; driverIndex++){
            for(int orderIndex = 0; orderIndex < orderDriverMatrix[0].length; orderIndex++){
                System.out.print(orderDriverMatrix[driverIndex][orderIndex] + " ");
            }
            System.out.println(" ");
        }
    }

    private static ArrayList<String> getRealIds(ArrayList<String> fakeIds) {
        ArrayList<String> realIds = new ArrayList<>();
        for(String fakeId : fakeIds) {
            realIds.add(fakeId.split("\\+")[0]);
        }
        return realIds;
    }

    public static float[][][] getProbabilities(ArrayList<String> demandIds, ArrayList<String> supplierIds){
        demandIds = getRealIds(demandIds);
        supplierIds = getRealIds(supplierIds);
        float[][][] probabilitiesOneToOne = new float[supplierIds.size()][demandIds.size()][GlobalVariables.GAIN.size()];
        ArrayList<FutureTask<ArrayList<float[]>>> futureTasks =  new ArrayList<>(supplierIds.size());
        for(int supplierIndex = 0; supplierIndex < supplierIds.size(); supplierIndex++){
            FutureTask<ArrayList<float[]>> task = new FutureTask<ArrayList<float[]>>(new ResponseTimePostThread(demandIds, supplierIds.get(supplierIndex), supplierIndex));
            futureTasks.add(task);
            threadPoolTaskExecutor.execute(task);
        }
        for (int supplierIndex = 0; supplierIndex < supplierIds.size(); supplierIndex++) {
            ArrayList<float[]> probs = null;
            try {
                probs = futureTasks.get(supplierIndex).get();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            for(int demandIndex = 0; demandIndex < demandIds.size(); demandIndex++) {
                for(int m = 0; m < GlobalVariables.RESPONSE_TIME_WINDOWS.length - 1; m++) {
                    for (int secondIndex = GlobalVariables.RESPONSE_TIME_WINDOWS[m]; secondIndex < GlobalVariables.RESPONSE_TIME_WINDOWS[m + 1]; secondIndex++) {
                        probabilitiesOneToOne[supplierIndex][demandIndex][secondIndex] = probs.get(demandIndex)[m] /
                                (GlobalVariables.RESPONSE_TIME_WINDOWS[m + 1] - GlobalVariables.RESPONSE_TIME_WINDOWS[m]);
                    }
                }
            }
        }
        return probabilitiesOneToOne;
    }

    public static RLOrderInfoForDispatching[] getStateValues(ArrayList<String> demandIds, ArrayList<String> supplierIds, int timeIndex) throws InterruptedException, ExecutionException {
        ArrayList<String> request_demandIds = getRealIds(demandIds);
        ArrayList<String> request_supplierIds = getRealIds(supplierIds);
        ArrayList<String> waitingRounds = new ArrayList<>(demandIds.size());
        for(int demandIndex = 0; demandIndex < demandIds.size(); demandIndex++){
            int waitTime = (int)(Duration.between(demandCache.getDemand(demandIds.get(demandIndex)).getTime(), LocalDateTime.now()).toMillis() / 1000);
            int waitingRound = waitTime / GlobalVariables.DISPATCHING_TIME_WINDOW + 1;
            waitingRounds.add(String.valueOf(waitingRound));
        }
        FutureTask<ArrayList<RLTransferOrderValueState>> valueFunctionTask = new FutureTask<>(
                new ValueFunctionPostThread(request_demandIds, waitingRounds, request_supplierIds,
                        String.valueOf(timeIndex)));
        threadPoolTaskExecutor.execute(valueFunctionTask);

        //记录每个订单的等待轮数及下一轮的最大收益
        RLOrderInfoForDispatching[] rlOrdersInfo = new RLOrderInfoForDispatching[demandIds.size()];
        ArrayList<RLTransferOrderValueState> states = valueFunctionTask.get();
        for(int demandIndex = 0; demandIndex < demandIds.size(); demandIndex++){
            int waitTime = (int)(Duration.between(demandCache.getDemand(demandIds.get(demandIndex)).getTime(), LocalDateTime.now()).toMillis() / 1000);
            int waitingRound = waitTime / GlobalVariables.DISPATCHING_TIME_WINDOW;
            if(waitingRound >= GlobalVariables.EXPIRED_TIME / GlobalVariables.DISPATCHING_TIME_WINDOW - 1){
                rlOrdersInfo[demandIndex] = new RLOrderInfoForDispatching(waitingRound, GlobalVariables.LINEAR_FINAL_ROUND_REWARD);
                continue;
            }
            rlOrdersInfo[demandIndex] = new RLOrderInfoForDispatching(waitingRound, states.get(demandIndex).getExpectedValue());
        }
        return rlOrdersInfo;
    }
}
